package com.funmi.lizip.security.config;

import com.funmi.lizip.security.filter.JsonAuthenticationFilter;
import com.funmi.lizip.security.filter.JwtTokenFilter;
import com.funmi.lizip.security.handler.*;
import com.funmi.lizip.security.service.ClimbUserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.Filter;

@Configuration
// 开启权限检查的注解
//@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 定制AuthenticationManager
    // 例如，添加自定义用户服务
     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         //配置DaoAuthenticationProvider中的UserDetailsService的实现类
        auth.userDetailsService(userDetailsService());
     }

    // 更改WebSecurity中的默认行为
    // 构建FilterChainProxy。一个FilterChainProxy包含多个过滤器链和一个Firewall
    // WebSecurity中的ignoring()用于配置不用安全处理的请求，如静态资源
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 忽略静态资源
        // web.ignoring().antMatchers("/js/**", "/css/**","/images/**");

    }

    /*@Override
    protected void configure(HttpSecurity http) throws Exception {
         http.csrf().disable();
         //http.authorizeRequests().anyRequest().authenticated();
    }*/
    // 更改HttpSecurity中的默认访问规则
    // 配置登录方式和访问权限
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 认证请求的配置
        http.authorizeRequests()
            // 1、访问权限
            //.antMatchers("/index").permitAll()   // permitAll表示放过匹配的请求
            .antMatchers("/login","/doc.html").permitAll()   // 放行Swagger
            .antMatchers("/jLogin/{username}", "/doc.html", "/doc.html/**", "/webjars/**", "/v2/**", "/swagger-resources",
                        "/swagger-resources/**", "/swagger-ui.html", "/swagger-ui.html/**","/v3/api-docs").permitAll()
                //.antMatchers("/**/**").permitAll()
                // 验证时有前缀ROLE_，/whoim需要ROLE_admin角色才能访问
            //.antMatchers("/whoim").hasRole("admin")
            // 在access中以表达式方式调用
            //.antMatchers("/whoim").access("hasRole('admin')")
            // /whoim需要ROLE_admin权限才能访问
            //.antMatchers("/whoim").hasAuthority("user.list")

            .anyRequest().authenticated()   // 所有请求都需要认证后才能访问
            .and()     // 结束前面配置，返回HttpSecurity对象并开启新的配置
            // 2、登录方式
                .formLogin()
                //.loginPage("/login.html")
                .loginProcessingUrl("/login")
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new MyLogoutSuccessHandler())
            .and()
            //Spring Security5默认开启CSRF，这里禁用CSRF，否则无法提交表单
            //开启CSRF后，在请求中必须包含CSRF的token，否则会抛出异常
            .csrf().disable()
            //开启跨域
            .cors()
            .and()
            //默认异常处理
            .exceptionHandling()
            .authenticationEntryPoint(climbAuthenticationEntryPoint())
            .accessDeniedHandler(climbAccessDeineHandler());

        // 替换 UsernamePasswordAuthenticationFilter
        http.addFilterAt(jsonAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        // 增加到UsernamePasswordAuthenticationFilter的前面
        http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public AccessDeniedHandler climbAccessDeineHandler() {
        return new ClimbAccessDeineHandler();
    }

    @Bean
    public GenericFilterBean jwtTokenFilter() {
         return new JwtTokenFilter();
    }

    @Bean
    public AuthenticationEntryPoint climbAuthenticationEntryPoint() {
         return new ClimbAuthenticationEntryPoint();
    }

    @Bean
    public Filter jsonAuthenticationFilter() throws Exception {
        JsonAuthenticationFilter jsonAuthenticationFilter = new JsonAuthenticationFilter();
        // 配置认证管理器
        jsonAuthenticationFilter.setAuthenticationManager(super.authenticationManagerBean());

        // 成功
        jsonAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler());
        // 失败
        jsonAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler());

        return jsonAuthenticationFilter;
    }

    @Bean
    public AuthenticationSuccessHandler myAuthenticationSuccessHandler() {
         return new MyAuthenticationSuccessHandler();
    }

    @Bean
    public AuthenticationFailureHandler myAuthenticationFailureHandler() {
        return new MyAuthenticationFailureHandler();
    }

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        return new ClimbUserDetailsServiceImpl();
    }



    @Bean
    public PasswordEncoder passwordEncoder() {
         return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}
